package org.smart4j.framework.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 类操作工具类
 * @author luoyong
 * @date 2018-11-19下午 5:44
 */
public class ClassUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);

    /**
     *  获取类加载器
     * @return 类加载器
     */
    public static ClassLoader getClassLoader(){
        return  Thread.currentThread().getContextClassLoader();
    }

    /**
     *  加载类
     * @param className 类的权限类目
     * @param isInitialized 是否初始化(是否执行静态代码块) true 执行
     * @return class对象
     */
    public static Class<?> loadClass(String className ,boolean isInitialized){
        Class<?> cls;
        try {
            cls = Class.forName(className,isInitialized,getClassLoader());
        } catch (ClassNotFoundException e) {
            LOGGER.error("load class failure",e);
            throw  new RuntimeException(e);
        }
        return cls;
    }

    /**
     * 获取指定包名下的所有类
     * @param packageName 包名
     * @return 类的集合
     */
    public static Set<Class<?>> getClassSet(String packageName){
        Set<Class<?>> classSet = new HashSet<Class<?>>(16);
        try {
            Enumeration<URL> urls= getClassLoader().getResources(packageName.replace(".","/"));
            while ( urls.hasMoreElements() ){
                URL url = urls.nextElement();
                if (url != null){
                    String protocol = url.getProtocol();
                    if ("file".equals(protocol)){
                        String basePackage = url.getPath().replaceAll("%20%", "");
                        addClass(classSet,basePackage,packageName);
                    }else if("jar".equals(protocol)){
                        JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection();
                        if (jarURLConnection != null){
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if (jarFile != null){
                                Enumeration<JarEntry> jarEntris = jarFile.entries();
                                while (jarEntris.hasMoreElements()){
                                    JarEntry jarEntry = jarEntris.nextElement();
                                    String jarName = jarEntry.getName();
                                    if (jarName.endsWith(".class")){
                                        String classname = jarName.substring(0, jarName.lastIndexOf(".")).replaceAll
                                                ("/", ".");
                                        doAddClass(classSet,classname);
                                    }
                                }
                            }
                        }
                    }

                }
            }
        } catch (IOException e) {
            LOGGER.error("get class set failure",e);
            throw  new RuntimeException(e);
        }
        return classSet;
    }


    public static void addClass(Set<Class<?>> classSet,String packagePath,String packageName){
         File[] files = new File(packagePath).listFiles(new FileFilter() {
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });
         for(File file : files){
             String fileName = file.getName();
             if (file.isFile()){
                 String className = fileName.substring(0, fileName.lastIndexOf("."));
                 if (StringUtils.isNotEmpty(packageName)){
                     className = packageName+"."+className;
                 }
                 doAddClass(classSet,className);
             }else{
                 String subPackagePath = fileName;
                 if (StringUtils.isNotEmpty(packagePath)){
                     subPackagePath = packagePath+"/"+subPackagePath;
                 }
                 String subPackageName = fileName;
                 if (StringUtils.isNotEmpty(packageName)){
                     subPackageName = packageName +"."+subPackageName;
                 }
                 addClass(classSet,subPackagePath,subPackageName);
             }
         }
    }

    public static void doAddClass(Set<Class<?>> classSet,String className){
        Class<?> cls = loadClass(className,true);
        classSet.add(cls);
    }
}
